Skip to content

config: migrate velocity.toml to velocity.yaml (Configurate) - #1824

Draft
electronicboy wants to merge 8 commits into
dev/4.0.0from
dev/3-configurate
Draft

electronicboy wants to merge 8 commits into
dev/4.0.0from
dev/3-configurate

Conversation

@electronicboy

@electronicboy electronicboy commented Jun 17, 2026 •

Copy link
Copy Markdown
Member

Summary

Finishes the Configurate migration: the proxy's configuration moves from velocity.toml (night-config) to velocity.yaml (Configurate 4 / YAML). This is a deliberately near-1:1 "yaml-ified" port — same keys, sections and documentation — with richer configuration left as a follow-up. Existing velocity.toml installs are migrated automatically on first start.

Draft / not for immediate merge. Opening this for documentation and review.

Rebased onto dev/4.0.0 (previously targeted dev/3.0.0).

What it does

  • ConfigurationLoader.loadConfiguration() is the new entry point, wired into VelocityServer startup and reload. It resolves velocity.yaml, migrating a legacy velocity.toml or writing the documented default on first start.
  • Model mapping via Configurate ObjectMapper over @ConfigSerializable VelocityConfiguration, with NamingSchemes.LOWER_CASE_DASHED so camelCase fields map to lower-case-dashed keys. @Setting covers the handful the scheme can't derive (kick-existing-players, packet-limiter, haproxy-protocol, accepts-transfers, query enabled/port/map).
  • Custom TypeSerializers for the dynamic-shaped sections that don't fit object mapping: Servers (named entries + the try list in one node), ForcedHosts, and PacketLimiterConfig (renamed keys).
  • Migration runs the existing night-config migrations to normalise the TOML, writes it out as YAML stamped config-version: 1, preserves a custom forwarding-secret-file location, and archives the old file as velocity.toml.migrated.

Ping passthrough (new since the rebase)

dev/4.0.0 reworked ping-passthrough from an enum into a record of five booleans (version, players, description, favicon, modinfo) plus a PingPassthroughMigration off the legacy string form. Carried across as:

  • The record is marked @ConfigSerializable and mapped directly by ObjectMapper — every component name is a single word, so LOWER_CASE_DASHED needs no @Setting overrides.
  • default-velocity.yaml gains the documented ping-passthrough: section, mirroring default-velocity.toml.
  • The TOML path needs no extra work: PingPassthroughMigration runs as part of the existing night-config migration chain before the config is dumped to YAML, so legacy string values are expanded on migration.

Design decisions worth a look

  • config-version is now an integer (baseline 1) so it can drive Configurate's versionedBuilder() when the first YAML-schema migration is needed. That hook is documented but intentionally inert today (no YAML versions to migrate between yet). Note this is independent of the TOML config-version (now "2.9"), which only gates the night-config migrations on the legacy path.
  • Documentation is preserved via a hand-curated default-velocity.yaml copied verbatim on fresh install. configurate-yaml 4.2.0 has no comment-writing support, so we do not rely on @Comment. A comment-supporting Configurate fork may be adopted later. Interim: programmatically (re)written files — i.e. migrated configs — won't carry comments until then.
  • No default-merge on load. Absent keys fall back to the model's field defaults (matching the old getOrElse), so existing files are never rewritten and their comments survive. Trade-off: newly added keys aren't auto-written into existing files until they're regenerated.
  • Example servers/forced-hosts live only in default-velocity.yaml, not in code. Code defaults are empty, so removing a section yields an empty collection instead of resurrecting example entries that reference servers the user doesn't have (which previously failed validation). Migration remains behavior-preserving — effective configs are unchanged when converting from TOML.

Testing

  • ConfigurationLoaderTest: loads the bundled default; round-trips a config with non-default values for every renamed/custom-mapped key (so a wrong mapping can't silently pass, now including the full ping-passthrough section); migrates a legacy velocity.toml (values carried, secret-file preserved, version stamped, old file archived, and a legacy ping-passthrough = "DESCRIPTION" expanded to description + modinfo); and verifies removed sections don't resurrect defaults.
  • Full proxy test suite + checkstyle green against dev/4.0.0.

Out of scope / follow-ups

  • Expanding configuration capabilities (the strategic reason for ObjectMapper).
  • Wiring the full versionedBuilder() once a real YAML-schema migration exists.
  • Removing night-config + LegacyConfigurationLoader once the migration path is retired.

🤖 Generated with Claude Code

electronicboy and others added 8 commits September 1, 2026 14:51
1:1 YAML port of default-velocity.toml with all documentation comments
preserved verbatim. config-version becomes an integer (baseline 1) for
Configurate's versioned transformation system; the `try` list nests under
`servers` to mirror the legacy [servers] table.

Part of the velocity.toml -> velocity.yml Configurate migration.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…tMapper

Wire up the Configurate ObjectMapper path for the new velocity.yml format:

- Make VelocityConfiguration ObjectMapper-friendly: no-arg constructor,
  non-final nested fields, transient on non-config fields (forwardingSecret,
  motdAsComponent, favicon), and drop the dead gson @expose annotations.
- Annotate Advanced/Query/Metrics @ConfigSerializable and add @setting for the
  keys the lower-case-dashed naming scheme can't derive (kick-existing-players,
  packet-limiter, haproxy-protocol, accepts-transfers, query enabled/port/map).
- Add ConfigurationLoader with a LOWER_CASE_DASHED ObjectMapper factory, a YAML
  loader builder, load/save helpers, and custom TypeSerializers for the dynamic
  sections that don't fit object mapping: Servers (entries + try), ForcedHosts,
  and PacketLimiterConfig (renamed keys).
- Add ConfigurationLoaderTest: loads the bundled default, and round-trips a
  config with non-default values for every renamed/custom-mapped key so a wrong
  mapping can't silently fall back to an identical default.

Part of the velocity.toml -> velocity.yml Configurate migration.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Implement the runtime entry point for the YAML config:

- ConfigurationLoader.loadConfiguration() resolves velocity.yml, migrating a
  legacy velocity.toml or writing the documented default on first start. Absent
  keys fall back to the model's field defaults (matching the old getOrElse
  behaviour), so existing files are never rewritten and their comments survive.
- Migration runs the legacy night-config migrations to normalise the TOML, then
  writes it as YAML stamped config-version=1, preserving a custom
  forwarding-secret-file location, and archives the old file as
  velocity.toml.migrated.
- Forwarding secret resolution mirrors the legacy path (env var, then the
  forwarding-secret-file, creating it if absent) and is injected via a new
  package-private setter; the secret stays out of velocity.yml.

Not yet wired into VelocityServer; that follows in the next change.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Route VelocityServer's startup and reload paths through
ConfigurationLoader.loadConfiguration(), so the proxy now reads velocity.yml
(migrating an existing velocity.toml on first start) instead of reading
velocity.toml directly. LegacyConfigurationLoader is retained behind the
migration path.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Move the example servers, try order, and forced hosts out of the model's
field defaults and into default-velocity.yml only. Previously, removing a
section caused the loader to substitute the bundled examples, which reference
servers the user may not have and then fail validation. With empty code
defaults, a removed or emptied section now yields an empty collection and the
examples are seeded solely on first-start from the documented default file.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Rename the proxy configuration file and bundled default resource from
velocity.yml/default-velocity.yml to velocity.yaml/default-velocity.yaml.
Third-party plugin descriptor files (plugin.yml, bungee.yml,
paper-plugin.yml) are unaffected.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
dev/4.0.0 turned ping-passthrough from an enum into a record of five
booleans (version/players/description/favicon/modinfo) with a
night-config migration off the legacy string form.

Mark the record @ConfigSerializable so Configurate's ObjectMapper maps
the section directly (all component names are already single words, so
LOWER_CASE_DASHED needs no @setting overrides), and replace the string
key in default-velocity.yaml with the documented section, mirroring
default-velocity.toml.

The TOML path needs no extra work: PingPassthroughMigration runs as part
of the existing night-config migration chain before the config is dumped
to YAML, so legacy values are expanded on migration.

Tests: the round-trip fixture now carries the section with all five
flags set, and the migration fixture starts from a legacy
ping-passthrough = "DESCRIPTION" and asserts it expands to
description + modinfo.
@electronicboy
electronicboy changed the base branch from dev/3.0.0 to dev/4.0.0 September 1, 2026 12:56
@electronicboy electronicboy changed the title config: migrate velocity.toml to velocity.yml (Configurate) config: migrate velocity.toml to velocity.yaml (Configurate) Sep 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant